#include <hd_led.h>
#include <hd_oled_protocol_i2c.h>
#include <pd_timer.h>
#include <pd_timer_it.h>
#include <pd_timer_encoder.h>
#include <stddef.h>
#include <pd_i2c_hd.h>
#include "main.h"
#include "delay.h"
#include "hd_oled.h"


#define ARRAY_SIZE(x)       sizeof(x) / sizeof(x[0])
#define USE_HARDWARE_SPI    1
#define USE_HARDWARE_I2C    1
int16_t speed;
LED led;
OLED oled;

extern void SystemClock_Config(void);

void oled_instance(OLED *oled);

TimerEncoder timerEncoder;

int main(void) {
    SystemClock_Config();

    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

    oled_instance(&oled);

    OLED_printf(&oled, 0, 0, "TIM Encoder");
    OLED_printf(&oled, 0, 32, "Speed:");
    OLED_refresh(&oled);

    // TIM3 Encoder Interface
    Timer tim3;
    Timer_constructor(&tim3, TIM3, 1 - 1, 65536 - 1);

    GPIO gpio[2];
    GPIO_constructor(gpio + 0, GPIOB, LL_GPIO_PIN_4);
    GPIO_constructor(gpio + 1, GPIOB, LL_GPIO_PIN_5);
    gpio[0].Alternate = LL_GPIO_AF_2;
    gpio[1].Alternate = LL_GPIO_AF_2;

    uint32_t channel[] = {LL_TIM_CHANNEL_CH1, LL_TIM_CHANNEL_CH2};
    TimerCh timerChannel;
    TimerCh_constructor(&timerChannel, gpio, ARRAY_SIZE(gpio), channel, ARRAY_SIZE(channel));

    TimerEncoder_constructor(&timerEncoder, &tim3, &timerChannel);
    TimerEncoder_init(&timerEncoder);

    // TIM2 IT
    Timer tim2;
    Timer_constructor(&tim2, TIM2, 8400 - 1, 10000 - 1);
    TimerIT timerIt;
    TimerIT_constructor(&timerIt, &tim2);
    TimerIT_init(&timerIt, TIM2_IRQn, 2, 2);

    while (1) {
        OLED_printf(&oled, 48, 32, "%5d", speed);
        OLED_refresh(&oled);
        delay_ms(100);
    }
}

void TIM2_IRQHandler(void) {
    if (LL_TIM_IsActiveFlag_UPDATE(TIM2)) {
        speed = TimerEncoder_getCounter(&timerEncoder);
        LL_TIM_ClearFlag_UPDATE(TIM2);
    }
}


void oled_instance(OLED *oled) {
    // I2C gpio init
    static GPIO oledSckGPIO, oledSdaGPIO;
    GPIO_constructor(&oledSckGPIO, GPIOB, LL_GPIO_PIN_10); // I2Cx_SCK
    GPIO_constructor(&oledSdaGPIO, GPIOB, LL_GPIO_PIN_11); // I2Cx_SDA

#ifdef USE_HARDWARE_I2C
    static I2CHardware i2c2;
    I2CHardware_constructor(&i2c2, I2C2, &oledSckGPIO, &oledSdaGPIO);
#elif USE_SIMULATE_I2C
    static I2CSimulate i2c2;
    I2CSimulate_constructor(&i2c2, &oledSckGPIO, &oledSdaGPIO);
#endif
    I2C_init((I2C *)&i2c2);

    // OLED protocol of I2C
    static OLEDProtocolI2C oledProtocolI2c;
    OLEDProtocolI2C_constructor(&oledProtocolI2c, (I2C *)&i2c2);

    // OLED font instance.
    static OLEDFont oledFont;
    OLEDFont_contructor(&oledFont, NULL);

    // OLED instance of 4-PIN(I2C)
    OLED_constructor(oled, (OLEDProtocol *)&oledProtocolI2c, &oledFont);

    OLED_init(oled);

    OLED_clear(oled);
    OLED_refresh(oled);
}
